home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume13 / korner < prev    next >
Encoding:
Internet Message Format  |  1988-01-31  |  29.7 KB

  1. Subject:  v13i045:  Convert (some) csh scripts to ksh scripts
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Bob Mcqueer <rtech!bobm>
  7. Posting-number: Volume 13, Issue 45
  8. Archive-name: korner
  9.  
  10. [  As Bob admits, this is a hack.  Still and all, it might help folks make
  11.    the great leap from csh to ksh.  I wouldn't know, and haven't tried
  12.    this.  --r$ ]
  13.  
  14. This program feeds CSH scripts to a copy of /bin/csh then analyzes
  15. the new aliases and variables that have been created, turning them
  16. into (nearly) equivalent KSH shell functions.
  17.  
  18. {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm
  19. ------ cut here -------
  20. #! /bin/sh
  21. # This is a shell archive, meaning:
  22. # 1. Remove everything above the #! /bin/sh line.
  23. # 2. Save the resulting text in a file.
  24. # 3. Execute the file with /bin/sh (not csh) to create the files:
  25. #    read.me
  26. #    Makefile
  27. #    korner.1
  28. #    diagnostic.c
  29. #    korner.c
  30. #    strtok.c
  31. #    func
  32. # This archive created: Sun Oct 25 11:39:24 1987
  33. export PATH; PATH=/bin:$PATH
  34. if test -f 'read.me'
  35. then
  36.     echo shar: will not over-write existing file "'read.me'"
  37. else
  38. cat << \SHAR_EOF > 'read.me'
  39.  
  40. The problem this stuff addresses:
  41.  
  42.     I find myself the lone ksh user in a csh community.  The tools
  43.     I have to use on a daily basis are set up by a set of csh "source"
  44.     files, which define some of those tools as csh aliases.  But,
  45.     dadburn it, I want to use ksh!  AND I don't want to have to keep
  46.     redoing my own setup files everytime somebody does something to
  47.     the "standard" environment setup files which everybody is supposed
  48.     to "source".  Take it as a given that I'm not going to convince
  49.     anybody that the tool set should be defined in such a way that it
  50.     is independent of which shell you run (the tools in question are
  51.     often aliases for things to convert your environment on the fly -
  52.     stuff like "eval `<gargle>`" where <gargle> is some script that
  53.     generates sources and setenv's).
  54.  
  55.     What I need is to be able to "source" csh scripts under ksh, doing
  56.     the same thing in my ksh environment that the script intended to
  57.     do under csh.
  58.  
  59. What we got here:
  60.  
  61.     What I do is define a ksh function for "source" which:
  62.  
  63.         1) uses csh to run the source file, dumping the resultant aliases,
  64.         variables, directory stack, etc. off to a temp file.
  65.  
  66.         2) runs a translator (the program "korner") over the temp file,
  67.         producing a ksh file to run with "."  Korner is in no way
  68.         shape or form intended to handle all the csh constructs
  69.         which could be shoveled into aliases - but it does know
  70.         how to convert simpler argument metasyntax.
  71.  
  72.         3) does a "." of the file produced in 2), and removes the temp's.
  73.  
  74.     There are LOTS of holes here - the thing is basically a hack - but I've
  75.     been using it and having it work.  Doing step 1) alleviates a LOT of
  76.     the problem - let csh figure out its own silly script files and you
  77.     only have to understand what finds its way into the types of aliases
  78.     you need to translate.  A pitfall, of course, would be source files
  79.     depending on environmental things set up by preceding source files
  80.     which don't get passed from ksh into csh (cdpath setting, previous
  81.     aliases, etc).   You would then have to make a source file of your
  82.     own which sourced all the others, thus letting csh setup the whole
  83.     mess all over again.
  84.  
  85.     This approach even seems to work fast enough to use it on the fly
  86.     as I do - you could also save the output to periodically generate
  87.     your own setup files.
  88.  
  89.     "func" contains my definition for the "source" function.  The variable
  90.     KD is defined for use in temp file names.  If you define the variable
  91.     KDOUT, the generated ksh "." files will get saved in there.  For my
  92.     purposes, I also need to recognize "setenv" and "unsetenv" for the
  93.     generated aliases.  You will note that it filters the printenv output
  94.     which gets to the translator.  The "source" files I'm using this on do
  95.     "setenv"'s of around 100 variables.  Not filtering out the ones that
  96.     the csh "source" didn't change was causing ksh to run out of space
  97.     for variable assignments by reassigning them all the time.
  98.  
  99.     korner.1 is the man page for "korner".  The "strtok.c" source
  100.     file may be dispensed with if you have a system strtok() call
  101.     available.
  102.  
  103.     To process the csh directory stack, it is expected that you have
  104.     defined a "pushd" in your ksh definitions.  See korner.1.
  105.  
  106. caveats:
  107.  
  108.     I made no attempt to be comprehensive.  I just did a quick look over
  109.     the csh man page, and translated the metasyntax that seemed reasonable
  110.     to translate in processing the aliases.
  111.  
  112.     It's strictly a hand tooled parser - no formal grammar definition or
  113.     anything like that.
  114.  
  115.     I've debugged this only to the extent that it works on the csh source
  116.     files I've had to use.
  117.  
  118.     It's only ever run in one environment - Pyramid OSx, generally in the
  119.     ucb universe, with what I believe is an early ksh version.  I haven't
  120.     had occasion to try this thing on other machines / versions of shells,
  121.     etc.  Some things, such as the expected output format of the various
  122.     csh commands, were determined simply by examining what the local csh
  123.     did, and slight differences in somebody else's csh might break it.
  124.  
  125.     Because of the above, it will likely have to be tweaked a bit to
  126.     get YOUR set of csh setup files to run through it.
  127.  
  128.     Let me know of significant improvements, fixes, etc.
  129.  
  130. Bob McQueer
  131. {amdahl, sun, mtxinu, hoptoad, cpsc6a}!rtech!bobm
  132. SHAR_EOF
  133. fi # end of overwriting check
  134. if test -f 'Makefile'
  135. then
  136.     echo shar: will not over-write existing file "'Makefile'"
  137. else
  138. cat << \SHAR_EOF > 'Makefile'
  139. OBJS = korner.o diagnostic.o strtok.o
  140.  
  141. CFLAGS = -O
  142.  
  143. korner: $(OBJS)
  144.     cc -o korner $(OBJS)
  145. SHAR_EOF
  146. fi # end of overwriting check
  147. if test -f 'korner.1'
  148. then
  149.     echo shar: will not over-write existing file "'korner.1'"
  150. else
  151. cat << \SHAR_EOF > 'korner.1'
  152. .tr ~
  153. .TH KORNER LOCAL 2/1/86
  154. .UC
  155. .SH NAME
  156. korner - ksh environment converter
  157. .SH SYNOPSIS
  158. .B korner
  159. [-dpushd] [-ssectchar] [-c] [-x] [-p] [-l] [-|infile] [outfile]
  160. .SH DESCRIPTION
  161. .I Korner
  162. takes input describing environment settings in formats typically produced by
  163. .I csh (1)
  164. and
  165. .I printenv (1)
  166. and produces ksh commands to reflect them.  The purpose is to allow ksh users
  167. to use csh "source" files, obtaining a ksh environment duplicating that set up
  168. for csh.
  169. .sp
  170. The -c option will cause the ksh CDPATH to be replaced rather than
  171. prefixed.  It is normally prefixed because csh does not pick up the
  172. ksh CDPATH value as its own setting, the way it does the PATH setting.
  173. .sp
  174. The -x option will cause
  175. .I korner
  176. NOT to export the variables it assigns from its
  177. .I printenv
  178. style input.  Export is the default because this is implied by use
  179. of the csh "setenv" builtin, and if the input really comes from
  180. .I printenv,
  181. the variables have to be exported for the
  182. .I printenv
  183. process to see them anyway.
  184. .sp
  185. The -p option will allow csh "prompt" settings to affect the ksh PS1 variable.
  186. Normally, these are thrown away since csh doesn't inherit the ksh prompt, and
  187. you don't want PS1 turned into the default csh "%".
  188. .sp
  189. The
  190. .I -d
  191. option will cause
  192. .I korner
  193. to use an alternate "pushd" command when processing the directory stack.
  194. The directory stack environment input is reproduced by generating a series
  195. of "pushd"'s, or the specified command.
  196. .sp
  197. The -l option causes unrecognized csh "set"'s to be assigned as unexported
  198. ksh variables.  They are normally ignored.
  199. .sp
  200. The -s option specifies some character other than the default "+" used
  201. to separate sections of
  202. .I korner
  203. input.  This is useful if the environment being processed insists on
  204. naming something "+".
  205. .sp
  206. If the input file is missing or given as "-", standard input is used.
  207. .sp
  208. If the output file is missing standard output is used.
  209. .SH INPUT~FORMAT
  210. Input to
  211. .I korner
  212. is in several sections, each beginning
  213. with a line starting with a "+" ( or the alternate character specified
  214. by the -s option).  Lines following the section delimiter are understood
  215. to represent a certain type of environment information until the next "+"
  216. line, or end of file.
  217. .sp
  218. The "+E" line signifies variable assignments in the format produced by the
  219. .I printenv
  220. command.  They will be converted into pairs of export and variable
  221. assignment statements.  If -x is set, the exports will not be done.
  222. .sp
  223. The "+A" line signifies csh
  224. aliases in the format produced by the "alias" listing.
  225. A ksh alias or function with the same name will be produced for each.
  226. If no quoting or arguments are required, an alias is produced, otherwise
  227. a function which attempts to mimic the
  228. csh alias using ksh argument syntax.
  229. .sp
  230. The "+D" line should actually only have one line following it, which is
  231. a directory stack, ala the csh "dirs" command.  The first word is assumed
  232. to be the current directory, and is thrown out if a "cwd" setting has been seen.
  233. The rest are used in reverse order
  234. to generate "pushd" commands for the ksh directory stack.
  235. .sp
  236. The "+S" signifies the results of a csh "set" command.  Selected items
  237. from the list which have ksh analogs are processed into settings of the
  238. appropriate ksh variable.  "cwd" is used to set the current directory via a "cd"
  239. command.  Use of "prompt" is optional, based on the "-p" flag.  "cdpath" is
  240. usually prefixed to an existing
  241. ksh CDPATH because invoking csh under ksh doesn't get the csh cdpath set
  242. up from the ksh one, as happens with "path" from PATH.  Using command line
  243. option "-c" makes the "cdpath" setting replace any existing CDPATH.
  244. Settings not recognized as something with a ksh analog are usually ignored, but
  245. may be turned into unexported variable assignments with the "-l" option.
  246. .sp
  247. A "+E" section should precede a "+S" section so that the csh setting of
  248. things like "home" can override an exported-in HOME variable.  A "+S"
  249. section should precede a "+D" section so that the HOME setting, if
  250. changed, will be correct for interpretation of tilde's.
  251. .sp
  252. The "cd" command generated by
  253. .I korner
  254. based on either the "cwd" setting or the start of the "+D" section
  255. is deferred to the end of its output.
  256. .SH DIAGNOSTICS
  257. Messages printed on standard error for failures and illegal input.
  258. .SH BUGS~AND~LIMITATIONS
  259. There are many things which cannot be handled for a variety of reasons.
  260. Attempts have been made to at least generate diagnostics for these cases.
  261. .sp
  262. It is possible to define variables with strange
  263. names, like "&", and actually get them into the environment table for
  264. .I printenv
  265. to see.  These will be thrown out with an error message.  Note that shell
  266. processed "variables" like "$#" don't turn up in
  267. .I printenv.
  268. .I Korner
  269. wants variable names beginning with a underscores or an alphabetic, and
  270. containing underscores and alphanumerics.
  271. .sp
  272. Variable assignments containing embedded newlines will be handled provided
  273. there is not an "=" somewhere after the newline.  In this situation, it
  274. is impossible to tell from the output of
  275. .I printenv
  276. whether you have a single variable containing an embedded newline, or
  277. two variable assignments.
  278. .I Korner
  279. simply assumes the latter, rather than trying to
  280. guess whether you REALLY have a variable named " ", or <newline><space><equals>
  281. was embedded in the preceding definition.
  282. .sp
  283. The metasyntax used by csh for describing aliases doesn't match up
  284. exactly with ksh function argument syntax, and involved uses of it won't work.
  285. .I Korner
  286. attempts to translate what it can into ksh arguments, and produces
  287. diagnostics for constructions that it doesn't understand.
  288. .sp
  289. Embedding csh built-ins in alias definitions will not work unless ksh
  290. has similar built-ins, or definitions are made for them.  "setenv"
  291. and "unsetenv" are two good examples.
  292. .I Korner
  293. does not attempt to generate diagnostics concerning built-ins.  The intent
  294. is that you will define ksh aliases / functions for the ones you need.
  295. .sp
  296. In particular, csh "flow-of-control" in aliases won't work, since
  297. .I korner
  298. does not know about "if", "else" "foreach", etc.
  299. .sp
  300. Embedded newlines inside alias definitions will cause problems.  Fortunately,
  301. csh does not reflect what kind of white space appeared in the alias definition,
  302. only producing a newline in the alias definition if there is a literal
  303. newline to be inserted somewhere.
  304. .sp
  305. Even with these severe limitations, the author has found that
  306. .I korner
  307. suffices to allow him to use tools and environment setup provided
  308. as csh "source" files under ksh.  Many csh "source" scripts only use
  309. complex csh interpretation to set up an environment, not to define
  310. aliases.  Simply allowing csh to do so, then "dumping" the result for
  311. .I korner
  312. to analyze gets around a lot of translation problems.
  313. .SH AUTHOR
  314. R. L. McQueer
  315. SHAR_EOF
  316. fi # end of overwriting check
  317. if test -f 'diagnostic.c'
  318. then
  319.     echo shar: will not over-write existing file "'diagnostic.c'"
  320. else
  321. cat << \SHAR_EOF > 'diagnostic.c'
  322. #include <stdio.h>
  323.  
  324. /*
  325. ** generic error message routines.  Diag_xxx, may be externally set by caller.
  326. **
  327. ** possible portability problem - use of several "long" arguments to pass
  328. ** stack through to underlying printf() family routine.
  329. */
  330.  
  331. /*
  332. **
  333. **    Copyright (c) 1987, Robert L. McQueer
  334. **        All Rights Reserved
  335. **
  336. ** Permission granted for use, modification and redistribution of this
  337. ** software provided that no use is made for commercial gain without the
  338. ** written consent of the author, that all copyright notices remain intact,
  339. ** and that all changes are clearly documented.  No warranty of any kind
  340. ** concerning any use which may be made of this software is offered or implied.
  341. **
  342. */
  343.  
  344. char *Diag_file = "";        /* filename for use in diagnostic message */
  345. int Diag_line = 1;        /* diagnostic line number */
  346. FILE *Diag_fp = stderr;        /* output stream for messages */
  347. char *Diag_cmd = "?";        /* command name for fatal() / usage() */
  348.  
  349. static int (*Fatcall)() = NULL;
  350.  
  351. /*
  352. ** print nonfatal diagnostic with line number from an input file.  Format
  353. ** compatible with "context"
  354. */
  355. diagnostic(s,a,b,c,d,e,f)
  356. char *s;
  357. long a,b,c,d,e,f;
  358. {
  359.     fprintf(Diag_fp,"%s line %d: ",Diag_file,Diag_line);
  360.     fprintf(Diag_fp,s,a,b,c,d,e,f);
  361.     fprintf(Diag_fp,"\n");
  362. }
  363.  
  364. /*
  365. ** print fatal error message and exit.  May call user set cleanup routine first.
  366. ** argument list passed to fatal() will also be passed to cleanup routine.
  367. */
  368. fatal (s,a,b,c,d,e,f)
  369. char *s;
  370. long a,b,c,d,e,f;
  371. {
  372.     fprintf (Diag_fp,"%s: ",Diag_cmd);
  373.     fprintf (Diag_fp,s,a,b,c,d,e,f);
  374.     fprintf (Diag_fp,"\n");
  375.     if (Fatcall != NULL)
  376.         (*Fatcall) (s,a,b,c,d,e,f);
  377.     exit (1);
  378. }
  379.  
  380. /*
  381. ** set cleanup routine for fatal() calls
  382. */
  383. fat_set (fn)
  384. int (*fn) ();
  385. {
  386.     Fatcall = fn;
  387. }
  388.  
  389. /*
  390. ** print usage message and exit.
  391. */
  392. usage (s,a,b,c,d,e,f)
  393. char *s;
  394. long a,b,c,d,e,f;
  395. {
  396.     fprintf (Diag_fp,"usage: %s ",Diag_cmd);
  397.     fprintf (Diag_fp,s,a,b,c,d,e,f);
  398.     fprintf (Diag_fp,"\n");
  399.     exit (1);
  400. }
  401. SHAR_EOF
  402. fi # end of overwriting check
  403. if test -f 'korner.c'
  404. then
  405.     echo shar: will not over-write existing file "'korner.c'"
  406. else
  407. cat << \SHAR_EOF > 'korner.c'
  408. /*
  409. **
  410. **    Copyright (c) 1987, Robert L. McQueer
  411. **        All Rights Reserved
  412. **
  413. ** Permission granted for use, modification and redistribution of this
  414. ** software provided that no use is made for commercial gain without the
  415. ** written consent of the author, that all copyright notices remain intact,
  416. ** and that all changes are clearly documented.  No warranty of any kind
  417. ** concerning any use which may be made of this software is offered or implied.
  418. **
  419. */
  420.  
  421. #include <stdio.h>
  422. #include <ctype.h>
  423. #include <sys/param.h>
  424.  
  425. char Pwd[MAXPATHLEN];
  426. char *Pushd = "pushd";
  427. char *Umsg = "[-dpushd] [-ssectionchar] [-c] [-x] [-p] [-l] [-|infile] [outfile]";
  428. char Sect = '+';
  429.  
  430. int Cdreplace = 0;
  431. int Doprompt = 0;
  432. int Xvar = 1;
  433. int Lvar = 0;
  434.  
  435. /*
  436. ** limits.  BIGBUFFER determines how much text for a single alias, shell
  437. ** variable, etc. can be handled.  DIRDEPTH determines the depth of csh pushd's
  438. ** which can be accomodated.
  439. */
  440. #define BIGBUFFER (MAXPATHLEN < 1800 ? 1800 : MAXPATHLEN)
  441. #define DIRDEPTH 120
  442.  
  443. /*
  444. ** characters significant inside double quotes, thus requiring backslashes
  445. */
  446. #define DQSIG "\\$`\""
  447.  
  448. extern char *Diag_cmd;
  449. extern char *Diag_file;
  450. extern int Diag_line;
  451.  
  452. main(argc,argv)
  453. int argc;
  454. char **argv;
  455. {
  456.     FILE *fpin;
  457.     FILE *fpout;
  458.     char *rindex();
  459.  
  460.     /*
  461.     ** set command for messages
  462.     */
  463.     if ((Diag_cmd = rindex(*argv,'/')) == NULL)
  464.         Diag_cmd = *argv;
  465.     else
  466.         ++Diag_cmd;
  467.     ++argv;
  468.  
  469.     /*
  470.     ** check options
  471.     */
  472.     while (argc > 1 && **argv == '-' && *((*argv)+1) != '\0')
  473.     {
  474.         --argc;
  475.         for (++(*argv); **argv != '\0'; ++(*argv))
  476.         {
  477.             switch(**argv)
  478.             {
  479.             case 'x':
  480.                 Xvar = 0;
  481.                 break;
  482.             case 'c':
  483.                 Cdreplace = 1;
  484.                 break;
  485.             case 'l':
  486.                 Lvar = 1;
  487.                 break;
  488.             case 'p':
  489.                 Doprompt = 1;
  490.                 break;
  491.             case 'd':
  492.                 ++(*argv);
  493.                 if (**argv == '\0')
  494.                 {
  495.                     if ((--argc) < 1)
  496.                         usage(Umsg);
  497.                     ++argv;
  498.                 }
  499.                 Pushd = *argv;
  500.                 *argv += strlen(*argv) - 1;
  501.                 break;
  502.             case 's':
  503.                 ++(*argv);
  504.                 if (**argv == '\0')
  505.                 {
  506.                     if ((--argc) < 1)
  507.                     usage(Umsg);
  508.                     ++argv;
  509.                 }
  510.                 Sect = **argv;
  511.                 *argv += strlen(*argv) - 1;
  512.                 break;
  513.             default:
  514.                 usage(Umsg);
  515.             }
  516.         }
  517.         ++argv;
  518.     }
  519.  
  520.     /*
  521.     ** set input stream, point Diag_file to name for diagnostics
  522.     */
  523.     if (argc > 1 && strcmp(*argv,"-") != 0)
  524.     {
  525.         if ((fpin = fopen(*argv,"r")) == NULL)
  526.             fatal("Can't open %s",*argv);
  527.         Diag_file = *argv;
  528.     }
  529.     else
  530.     {
  531.         fpin = stdin;
  532.         Diag_file = "<STDIN>";
  533.     }
  534.  
  535.     /*
  536.     ** set ouput stream.
  537.     */
  538.     if (argc > 2)
  539.     {
  540.         ++argv;
  541.         if ((fpout = fopen(*argv,"w")) == NULL)
  542.             fatal("Can't open %s",*argv);
  543.     }
  544.     else
  545.         fpout = stdout;
  546.  
  547.     parse(fpin,fpout);
  548. }
  549.  
  550. /*
  551. ** output string intended to be inside double quotes, thus requiring
  552. ** backslashes
  553. */
  554. dq_out(f,str)
  555. FILE *f;
  556. char *str;
  557. {
  558.     char *sp;
  559.     char sc;
  560.  
  561.     for (;;)
  562.     {
  563.         for (sp = str; *sp != '\0' && index(DQSIG,*sp) == NULL; ++sp)
  564.             ;
  565.         if (*sp != '\0')
  566.         {
  567.             sc = *sp;
  568.             *sp = '\0';
  569.             fprintf(f,"%s\\%c",str,sc);
  570.             str = sp+1;
  571.             continue;
  572.         }
  573.         break;
  574.     }
  575.     fprintf(f,"%s",str);
  576. }
  577.  
  578. /*
  579. ** generic variable assignment
  580. */
  581. var_assign(f,name,setting)
  582. FILE *f;
  583. char *name;
  584. char *setting;
  585. {
  586.     fprintf(f,"%s=\"",name);
  587.     dq_out(f,setting);
  588.     fprintf(f,"\"\n");
  589. }
  590.  
  591. /*
  592. ** find the starting delimiter of a name assignment, or return NULL
  593. ** designed to fail for wierd names that won't make proper
  594. ** ksh names
  595. */
  596. char *
  597. namstart(str,delim)
  598. char *str;
  599. char delim;
  600. {
  601.     char *eq;
  602.  
  603.     if (*str != '_' && !isalpha(*str))
  604.         return (NULL);
  605.  
  606.     for (eq=str+1; *eq != delim; ++eq)
  607.     {
  608.         if (*eq == '\0')
  609.             return (NULL);
  610.         if (*eq != '_' && !isalnum(*eq))
  611.             return (NULL);
  612.     }
  613.  
  614.     return (eq);
  615. }
  616.  
  617. /*
  618. ** process input from various enviroment commands, writing out ksh
  619. ** commands to simulate that environment.
  620. **
  621. ** Input consists of lines beginning with "+" and a code which signifies
  622. ** what all following lines up to the next leading "+" contain:
  623. **
  624. **    +E:
  625. **        environment variables, printed as <var>=<string>, as from
  626. **        the "printenv" command.  Turned into pairs of exports and
  627. **        variable assignments.
  628. **
  629. **    +S:
  630. **        csh "set" command results.  Specific variables which have
  631. **        ksh analogs (term, path, cdpath, home, cwd) handled specially,
  632. **        others turned into straight variable assignments.  The
  633. **        expected format is <name><tab><def>
  634. **
  635. **    +D:
  636. **        followed by a directory stack list, as from the csh "dirs"
  637. **        command.  Turned into pushd commands.  The directory stack
  638. **        is assumed to be separated by spaces, with pwd as the first
  639. **        item, followed by the top of the stack.
  640. **
  641. **    +A:
  642. **        csh style aliases.  Turned into ksh aliases and functions,
  643. **        as much as possible.  The expected format is <name><tab><def>.
  644. **
  645. ** Note that the "+S" input should precede the "+D" input for the latter to
  646. ** interpret ~ properly if "home" has been changed.  "+E" should also precede
  647. ** the "+S" section to allow csh set commands to override ksh variables
  648. ** imported into another environment.
  649. */
  650. static
  651. parse(fin,fout)
  652. FILE *fin;
  653. FILE *fout;
  654. {
  655.     char state;
  656.     char bufr[BIGBUFFER+1];
  657.     char *index();
  658.     char *ptr;
  659.     char *slash;
  660.     char sc;
  661.     int nolook;
  662.  
  663.     state = ' ';
  664.     Diag_line = 0;
  665.     Pwd[0] = '\0';
  666.  
  667.     /*
  668.     ** we keep a lookahead record when needed rather than seeking back
  669.     ** because we may be reading from stdin.
  670.     */
  671.     nolook = 1;
  672.     for (;;)
  673.     {
  674.         if (nolook)
  675.         {
  676.             if (fgets(bufr,BIGBUFFER,fin) == NULL)
  677.                 break;
  678.             bufr[strlen(bufr)-1] = '\0';
  679.             ++Diag_line;
  680.         }
  681.         else
  682.             nolook = 1;
  683.         if (bufr[0] == Sect)
  684.         {
  685.             state = bufr[1];
  686.             if (index("ESAD",state) == NULL)
  687.                 diagnostic("Unknown section type: %c%c",
  688.                                 Sect, state);
  689.             continue;
  690.         }
  691.         switch (state)
  692.         {
  693.         case 'E':
  694.             if ((ptr = namstart(bufr,'=')) == NULL)
  695.             {
  696.                 diagnostic("Bad variable definition");
  697.                 break;
  698.             }
  699.  
  700.             *ptr = '\0';
  701.             if (Xvar)
  702.                 fprintf(fout,"export %s\n%s=\"",bufr,bufr);
  703.             else
  704.                 fprintf(fout,"%s=\"",bufr);
  705.             ++ptr;
  706.  
  707.             /*
  708.             ** Read ahead a record, and if it doesn't appear
  709.             ** to be a new variable assignment or a state change,
  710.             ** stick in backslash-newline and continue processing
  711.             */
  712.             for (;;)
  713.             {
  714.                 dq_out(fout,ptr);
  715.                 if (fgets(bufr,BIGBUFFER,fin) == NULL)
  716.                     break;
  717.                 bufr[strlen(bufr)-1] = '\0';
  718.                 ++Diag_line;
  719.  
  720.                 /*
  721.                 ** we don't use namstart here because it's
  722.                 ** better to have a odd variable name fail
  723.                 ** than have it appended to a good assignment.
  724.                 ** Of course, now we miss legitimate '='
  725.                 ** characters following embedded newlines
  726.                 */
  727.                 if (bufr[0] == Sect || index (bufr,'=') != NULL)
  728.                 {
  729.                     nolook = 0;
  730.                     break;
  731.                 }
  732.                 fprintf(fout,"\\\n");
  733.                 ptr = bufr;
  734.             }
  735.             fprintf(fout,"\"\n");
  736.             break;
  737.         case 'A':
  738.             if ((ptr = namstart(bufr,'\t')) == NULL)
  739.             {
  740.                 diagnostic("Bad alias name");
  741.                 break;
  742.             }
  743.             *ptr = '\0';
  744.             ++ptr;
  745.             al_parse(bufr,ptr,fout);
  746.             break;
  747.         case 'S':
  748.             if ((ptr = namstart(bufr,'\t')) == NULL)
  749.             {
  750.                 diagnostic("Bad \"set\" name");
  751.                 break;
  752.             }
  753.             *ptr = '\0';
  754.             ++ptr;
  755.             set_check(bufr,ptr,fout);
  756.             break;
  757.         case 'D':
  758.             dir_list(bufr,fout);
  759.             break;
  760.         default:
  761.             break;
  762.         }
  763.     }
  764.  
  765.     /*
  766.     ** If we had pwd settings, issue a cd as the last thing
  767.     */
  768.     if (Pwd[0] != '\0')
  769.         fprintf(fout,"cd %s\n",Pwd);
  770. }
  771.  
  772. /*
  773. ** perform pushd's to get csh dir stack on ksh - must be pushed in reverse
  774. ** order.  Having a limit built in is hardly elegant, but in practical
  775. ** terms, its hard to imagine directory stacks being useful after getting
  776. ** past a few directories deep.  Plus, a rather large limit is cheap in terms
  777. ** storage.
  778. */
  779. static
  780. dir_list(s,f)
  781. char *s;
  782. FILE *f;
  783. {
  784.     char *ptr;
  785.     char *strtok();
  786.     char *dir[DIRDEPTH];
  787.     int numdir;
  788.  
  789.     /*
  790.     ** first item is pwd.  If we haven't set this because of a prior
  791.     ** setting (like "cwd" in a +S section), do so.
  792.     */
  793.     ptr = strtok(s," ");
  794.     if (ptr != NULL && Pwd[0] == '\0')
  795.         strcpy(Pwd,ptr);
  796.  
  797.     numdir = 0;
  798.     for (ptr = strtok(NULL," "); ptr != NULL; ptr = strtok(NULL," "))
  799.     {
  800.         if (numdir >= DIRDEPTH)
  801.         {
  802.             diagnostic("directory stack too deep - truncating");
  803.             break;
  804.         }
  805.         dir[numdir] = ptr;
  806.         ++numdir;
  807.     }
  808.  
  809.     /*
  810.     ** push in reverse order to get onto ksh stack
  811.     ** We assume pushd handles ~ notation.  As long as csh set
  812.     ** command is processed before directory stack, changes of
  813.     ** HOME will also be taken care of.
  814.     */
  815.     for (--numdir; numdir >= 0; --numdir)
  816.         fprintf (f,"%s %s\n",Pushd,dir[numdir]);
  817. }
  818.  
  819. /*
  820. ** handle a csh alias
  821. */
  822. static
  823. al_parse(name,cdef,f)
  824. char *name;
  825. char *cdef;
  826. FILE *f;
  827. {
  828.     int wantfunc;
  829.     char kdef[BIGBUFFER];
  830.  
  831.     if (! al_xln(cdef, kdef, &wantfunc))
  832.     {
  833.         /*
  834.         ** explicit diagnostic for first error will have also
  835.         ** been produced.
  836.         */
  837.         diagnostic("ALIAS %s=\"%s\" can't be translated\n",name,cdef);
  838.         return;
  839.     }
  840.  
  841.     if (wantfunc)
  842.         fprintf(f,"function %s\n{\n\t%s\n}\n",name,kdef);
  843.     else
  844.     {
  845.         fprintf(f,"alias %s=\"",name);
  846.         dq_out(f,kdef);
  847.         fprintf(f,"\"\n");
  848.     }
  849. }
  850.  
  851. /*
  852. ** translate subset of csh alias syntax into ksh.  If arguments or quotes
  853. ** are required, returned flag is TRUE, indicating need to define a function
  854. ** rather than an alias.  Diagnostics produced before error returns.
  855. */
  856. static
  857. al_xln(in,out,flag)
  858. char *in, *out;
  859. int *flag;
  860. {
  861.     char *index;
  862.     int iscan;
  863.  
  864.     if (*in == '(')
  865.         ++in;
  866.  
  867.     *flag = 0;
  868.  
  869.     for (; *in != '\0'; ++in)
  870.     {
  871.         switch(*in)
  872.         {
  873.  
  874.         /*
  875.         ** take care of csh level of backslashing
  876.         ** for literal !'s, etc.
  877.         */
  878.         case '\\':
  879.             ++in;
  880.             if (*in == '\0')
  881.             {
  882.                 diagnostic("Trailing backslash");
  883.                 return (0);
  884.             }
  885.             sprintf(out,"\\%c", *in);
  886.             out += 2;
  887.             break;
  888.  
  889.         /*
  890.         ** csh meta-syntax
  891.         */
  892.         case '!':
  893.             *flag = 1;
  894.             if ((iscan = meta(in+1,out)) <= 0)
  895.                 return (0);
  896.             in += iscan;
  897.             out += strlen(out);
  898.             break;
  899.  
  900.         /*
  901.         ** set flag for double or single quote escaping
  902.         */
  903.         case '"':
  904.         case '\'':
  905.             *flag = 1;    /* fall through */
  906.  
  907.         default:
  908.             *out = *in;
  909.             ++out;
  910.             break;
  911.         }
  912.     }
  913.  
  914.     if (out[-1] == ')')
  915.         --out;
  916.     *out = '\0';
  917.     return (1);
  918. }
  919.  
  920. /*
  921. ** translates what csh meta-syntax can be translated.  Returns number
  922. ** of characters scanned from input, 0 for untranslatable metacharacters
  923. ** Diagnostics produced at point of discerning error.
  924. */
  925. static
  926. meta(in,buf)
  927. char *in;
  928. char *buf;
  929. {
  930.     int a1, a2;
  931.     int len;
  932.  
  933.     switch (*in)
  934.     {
  935.     case '*':
  936.         strcpy(buf,"$*");
  937.         return (1);
  938.     case '^':
  939.         strcpy(buf,"$1");
  940.         return (1);
  941.     case ':':
  942.         ++in;
  943.         len = 2;
  944.         switch(*in)
  945.         {
  946.         case '*':
  947.             strcpy(buf,"$*");
  948.             return (2);
  949.         case '^':
  950.             a1 = 1;
  951.             break;
  952.         case '-':
  953.             a1 = 0;
  954.             --in;
  955.             len = 1;
  956.             break;
  957.         default:
  958.  
  959.             /*
  960.             ** only handle numeric parameters 0 - 9.
  961.             */
  962.             if (isdigit(*in))
  963.             {
  964.                 if (isdigit(in[1]))
  965.                 {
  966.                     diagnostic("Parameter out of range");
  967.                     return(0);
  968.                 }
  969.                 a1 = *in - '0';
  970.                 break;
  971.             }
  972.             diagnostic("Non-numeric '%c' following ':'", *in);
  973.             return (0);
  974.         }
  975.         break;
  976.     default:
  977.         diagnostic("Unknown metacharacter '%c'",*in);
  978.         return (0);
  979.     }
  980.  
  981.     /*
  982.     ** only the : case gets here - a1 is set to number, len to
  983.     ** parsed character count.  We now handle ranges.  Once again
  984.     ** arguments > $9 are returned as 0.
  985.     */
  986.     ++in;
  987.     if (*in != '-')
  988.     {
  989.         sprintf(buf,"$%d",a1);
  990.         return (len);
  991.     }
  992.     ++in;
  993.     if (! isdigit(*in))
  994.     {
  995.         diagnostic ("Bad parameter range, numeric expected after '-'");
  996.         return (0);
  997.     }
  998.     if (isdigit(in[1]))
  999.     {
  1000.         diagnostic("Parameter out of range");
  1001.         return(0);
  1002.     }
  1003.     a2 = *in - '0';
  1004.     len += 2;
  1005.     if (a2 < a1)
  1006.     {
  1007.         diagnostic("Bad parameter range");
  1008.         return (0);
  1009.     }
  1010.     for ( ; a1 <= a2; ++a1)
  1011.     {
  1012.         sprintf(buf,"$%d ",a1);
  1013.         buf += 3;
  1014.     }
  1015.     --buf;
  1016.     *buf = '\0';
  1017.     return (len);
  1018. }
  1019.  
  1020. /*
  1021. ** Some of the sets have meanings assigned to other names in ksh.  We
  1022. ** translate these, and simply make unexported assignments for the remainder.
  1023. ** Some of these depend on proper interaction between ksh and the spawned csh,
  1024. ** and are hard to deal with.  We usually cheat on CDPATH by attaching the
  1025. ** csh definitions to the ksh ones, since the ksh CDPATH doesn't get passed
  1026. ** to the csh environment.
  1027. */
  1028. static
  1029. set_check(name,setting,f)
  1030. char *name;
  1031. char *setting;
  1032. FILE *f;
  1033. {
  1034.     char *colonize();
  1035.  
  1036.     /*
  1037.     ** TERM setting
  1038.     */
  1039.     if (strcmp(name,"term") == 0)
  1040.     {
  1041.         var_assign(f,"TERM",setting);
  1042.         return;
  1043.     }
  1044.     /*
  1045.     ** pwd is tracked by csh cwd.  We set this as a global variable,
  1046.     ** generate the cd as the last thing, so that it doesn't matter
  1047.     ** whether or not this comes before pushd calls.
  1048.     */
  1049.     if (strcmp(name,"cwd") == 0)
  1050.     {
  1051.         strcpy(Pwd,setting);
  1052.         return;
  1053.     }
  1054.     /*
  1055.     ** home = HOME
  1056.     */
  1057.     if (strcmp(name,"home") == 0)
  1058.     {
  1059.         var_assign(f,"HOME",setting);
  1060.         return;
  1061.     }
  1062.     /*
  1063.     **  if we're taking it, prompt = PS1
  1064.     */
  1065.     if (Doprompt && strcmp(name,"prompt") == 0)
  1066.     {
  1067.         var_assign(f,"PS1",setting);
  1068.         return;
  1069.     }
  1070.     /*
  1071.     **  translate path syntax, place in PATH
  1072.     */
  1073.     if (strcmp(name,"path") == 0)
  1074.     {
  1075.         var_assign(f,"PATH",colonize(setting));
  1076.         return;
  1077.     }
  1078.     /*
  1079.     ** prepend translated csh cdpath setting to any existing ksh CDPATH
  1080.     ** unless Cdreplace flag is set.
  1081.     */
  1082.     if (strcmp(name,"cdpath") == 0)
  1083.     {
  1084.         setting = colonize(setting);
  1085.         if (Cdreplace)
  1086.             var_assign(f,"CDPATH",setting);
  1087.         else
  1088.         {
  1089.             fprintf(f,"if [ -n \"$CDPATH\" ]\nthen\n\tCDPATH=\"");
  1090.             dq_out(f,setting);
  1091.             fprintf(f,":$CDPATH\"\nelse\n\tCDPATH=\"");
  1092.             dq_out(f,setting);
  1093.             fprintf(f,"\"\nfi\n");
  1094.         }
  1095.         return;
  1096.     }
  1097.     /*
  1098.     ** shell (lower case) is probably set to /usr/ucb/csh.  set it to
  1099.     ** $SHELL (upper case).  Don't use var_assign because that will
  1100.     ** set it to a literal "$SHELL".  This is a "local" variable, so
  1101.     ** only do this if Lvar anyway.
  1102.     */
  1103.     if (Lvar && strcmp(name,"shell") == 0)
  1104.     {
  1105.         fprintf(f,"shell=\"$SHELL\"\n");
  1106.         return;
  1107.     }
  1108.  
  1109.     /*
  1110.     ** optional unexported variable
  1111.     */
  1112.     if (Lvar)
  1113.         var_assign(f,name,setting);
  1114. }
  1115.  
  1116. /*
  1117. ** Translate csh paths to ksh syntax.  Done over top of old string.
  1118. ** A small wrinkle - this routine will result in all pwd's in paths
  1119. ** being represented by '.' instead of an empty entry, because of csh
  1120. ** using "." in describing paths.
  1121. */
  1122. static char *
  1123. colonize(cp)
  1124. char *cp;
  1125. {
  1126.     char *ptr;
  1127.  
  1128.     if (*(ptr = cp) == '(')
  1129.     {
  1130.         ++cp;
  1131.         ++ptr;
  1132.     }
  1133.  
  1134.     /* spaces become colons, terminate on EOS or parentheses */
  1135.     for ( ; *cp != '\0' && *cp != ')'; ++cp)
  1136.         if (*cp == ' ')
  1137.             *cp = ':';
  1138.  
  1139.     /* terminate string & delete any trailing colons */
  1140.     for (*cp = '\0'; cp != ptr && *(--cp) == ':'; *cp = '\0')
  1141.         ;
  1142.  
  1143.     return (ptr);
  1144. }
  1145. SHAR_EOF
  1146. fi # end of overwriting check
  1147. if test -f 'strtok.c'
  1148. then
  1149.     echo shar: will not over-write existing file "'strtok.c'"
  1150. else
  1151. cat << \SHAR_EOF > 'strtok.c'
  1152. #include <stdio.h>
  1153.  
  1154. static char *Save=NULL;
  1155.  
  1156. char *
  1157. strtok(str,delim)
  1158. char *str, *delim;
  1159. {
  1160.     char *tokstart, *tokend, *first_ch (), *last_ch();
  1161.  
  1162.     if (str != NULL)
  1163.         Save = str;
  1164.  
  1165.     if (Save == NULL)
  1166.         return (NULL);
  1167.  
  1168.     tokstart = first_ch (Save, delim);
  1169.     tokend = last_ch (tokstart, delim);
  1170.     Save = first_ch (tokend, delim);
  1171.     *tokend = '\0';
  1172.  
  1173.     if (*tokstart == '\0')
  1174.         return (NULL);
  1175.  
  1176.     return (tokstart);
  1177. }
  1178.  
  1179. static char *
  1180. first_ch (str,delim)
  1181. char *str,*delim;
  1182. {
  1183.     char *index ();
  1184.     char *f;
  1185.  
  1186.     for (f = str; *f != '\0' && index(delim,*f) != NULL; ++f)
  1187.         ;
  1188.  
  1189.     return (f);
  1190. }
  1191.  
  1192. static char *
  1193. last_ch (str,delim)
  1194. char *str,*delim;
  1195. {
  1196.     char *index ();
  1197.     char *f;
  1198.  
  1199.     for (f = str; *f != '\0' && index(delim,*f) == NULL; ++f)
  1200.         ;
  1201.  
  1202.     return (f);
  1203. }
  1204. SHAR_EOF
  1205. fi # end of overwriting check
  1206. if test -f 'func'
  1207. then
  1208.     echo shar: will not over-write existing file "'func'"
  1209. else
  1210. cat << \SHAR_EOF > 'func'
  1211. function setenv
  1212. {
  1213.     export $1
  1214.     eval $1=\"$2\"
  1215. }
  1216.  
  1217. function unsetenv
  1218. {
  1219.     eval $1=\"\"
  1220. }
  1221.  
  1222. KD=/tmp/kd$$
  1223.  
  1224. function source
  1225. {
  1226.     if [ -s $1 ]
  1227.     then
  1228.         printenv >$KD.pe
  1229.         csh -c "source $1; echo +E >$KD.csh; /usr/ucb/printenv | comm  -23 - $KD.pe >>$KD.csh; echo +A >>$KD.csh; alias >>$KD.csh; echo +S >>$KD.csh; set >>$KD.csh; echo +D >>$KD.csh; dirs >>$KD.csh"
  1230.         korner $KD.csh $KD.env
  1231.         . $KD.env
  1232.         if [ -n "$KDOUT" ]
  1233.         then
  1234.             cat $KD.env >>$KDOUT
  1235.         fi
  1236.         rm $KD.*
  1237.     else
  1238.         echo source: $1 not found
  1239.     fi
  1240. }
  1241. SHAR_EOF
  1242. fi # end of overwriting check
  1243. #    End of shell archive
  1244. exit 0
  1245.